use axum::response::IntoResponse;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::value::Value;
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::collections::HashMap;
use time::OffsetDateTime;

use crate::emitter::EventName;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Serialize_repr, Deserialize_repr)]
pub enum ErrorCode {
    Unknown,
    InternalServerError,
    RateLimited,
    NotFound,
    Forbidden,
    BadRequest,
    Unauthorized,
    RpcError,
    WsError,
    ThirdPartyFail,
    UnknownUser,
    ShortLinkTaken,
    UnknownShortDomain,
    UnknownLibrary,
    TokenInvalid,
    UnknownResource,
    UnknownTag,
    PermissionDenied,
    UnknownComment,
    UnknownBot,
    UnknownServer,
    UnknownBadge,
    UserAlreadyHasABadge,
    InvalidInviteCode,
    ServerAlreadyExists,
    BotNotPresentOnQueueServer,
    UnknownUp,
    TooManyUps,
    InvalidStatus,
    UnknownReport,
    UnsupportedMediaType,
    UnknownApplication,
    AutomatedRequestsNotAllowed,
    InvalidRating,
    DuplicateBot,
}

#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
#[error("{code:?}: {message}")]
pub struct BcError {
    pub code: ErrorCode,
    pub message: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Response<T> {
    pub result: Option<T>,
    pub errors: Vec<BcError>,
    pub ok: bool,
    pub service: String,
}

impl<T: DeserializeOwned> Response<T> {
    pub fn json(self) -> axum::Json<Response<T>> {
        axum::Json(self)
    }
}

impl<T: Clone> From<Response<T>> for Result<T, Vec<BcError>> {
    fn from(val: Response<T>) -> Result<T, Vec<BcError>> {
        if val.ok {
            Ok(val.result.unwrap())
        } else {
            Err(val.errors)
        }
    }
}

impl<T: Clone> From<Result<T, BcError>> for Response<T> {
    fn from(value: Result<T, BcError>) -> Self {
        let mut errs = Vec::new();
        if let Err(e) = value.clone() {
            errs.push(e);
        }
        Self {
            result: value.clone().ok(),
            errors: errs,
            ok: value.is_ok(),
            service: String::from("notifications"),
        }
    }
}

impl<T: Clone> From<Result<T, Vec<BcError>>> for Response<T> {
    fn from(value: Result<T, Vec<BcError>>) -> Self {
        let errs = if let Err(e) = value.clone() {
            e
        } else {
            Vec::new()
        };
        Self {
            result: value.clone().ok(),
            errors: errs,
            ok: value.is_ok(),
            service: String::from("notifications"),
        }
    }
}

#[repr(u8)]
#[derive(Clone, Copy, Debug, Serialize_repr, Deserialize_repr)]
pub enum ResourceStatus {
    Hidden,
    Public,
    Banned,
    Pending,
    Denied,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Resource {
    pub id: String,
    pub name: String,
    #[serde(rename = "shortDescription")]
    pub short_description: String,
    pub description: String,
    pub avatar: Option<String>,
    #[serde(rename = "shortLink")]
    pub short_link: Option<String>,
    #[serde(rename = "inviteLink")]
    pub invite_link: String,
    #[serde(rename = "standard_banner_id")]
    pub standard_banner_id: u32,
    #[serde(rename = "status")]
    pub status: ResourceStatus,
    pub premium: PremiumResource,
    pub owner: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PremiumResource {
    pub active: bool,
    #[serde(rename = "splashURL")]
    pub splash_url: Option<String>,
    #[serde(rename = "autoFetch")]
    pub auto_fetch: bool,
    #[serde(rename = "bannerURL")]
    pub banner_url: Option<String>,
}

#[derive(Clone, Debug, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum BotLibrary {
    NotSpecified,
    Discord4J,
    Discordcr,
    DiscordGO,
    Discordoo,
    DSharpPlus,
    DiscordJS,
    DiscordNet,
    Eris,
    Javacord,
    JDA,
    Other,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WebhookSettings {
    pub enabled: bool,
    pub headers: HashMap<String, String>,
    pub url: Option<String>,
}

#[repr(u8)]
#[derive(Copy, Clone, Debug, Serialize_repr, Deserialize_repr)]
pub enum BotTags {
    Moderation,
    Combine,
    Utils,
    Fun,
    Music,
    Economics,
    Logs,
    Ranking,
    Nsfw,
    Settings,
    RolePlay,
    Memes,
    Games,
    Ai,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Bot {
    #[serde(flatten)]
    pub resource: Resource,
    pub prefix: String,
    pub discriminator: String,
    #[serde(rename = "createDate", with = "time::serde::iso8601")]
    pub created_at: OffsetDateTime,
    #[serde(rename = "supportServerInviteCode")]
    pub support_server_invite_code: Option<String>,
    pub library: Option<BotLibrary>,
    pub guilds: u64,
    pub members: u64,
    pub shards: u32,
    pub website: Option<String>,
    #[serde(rename = "webhookSettings")]
    pub webhook_settings: WebhookSettings,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Stats {
    pub shards: u32,
    pub guilds: u64,
    pub members: u64,
}

#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum NotifyType {
    BotApproved,
    BotRejected,
    BotUpdated,
    BotStatusChanged,
    BotDeleted,
    BotAdded,
    BotUpAdded,
    BotUpRemoved,
    ServerAdded,
    ServerRemoved,
    ServerUpdated,
    ServerUpAdded,
    ServerUpRemoved,
    PremiumPurchased,
    PremiumExpired,
    CommentAdded,
    CommentRemoved,
    CommentUpdated,
    CommentLiked,
    ReportAdded,
    ReportRejected,
    ReportApproved,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Notify {
    #[serde(rename = "type")]
    pub typ: String,
    pub payload: Value,
    pub affected: String,
    pub user: String,
    pub happened: i64,
    #[serde(rename = "webhookSettings")]
    pub webhook_settings: Option<WebhookSettings>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TokenType {
    Bot,
    Bearer,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JwtToken {
    pub tokenn: String,
    pub id: String,
    pub redirect: String,
    #[serde(rename = "type")]
    pub typ: TokenType,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenInfo {
    pub token: JwtToken,
    pub bot: Bot,
}
